// Copyright (c) 2023 Presto Labs Pte. Ltd.
// Author: jhkim

#include "cc/appcoin2/research/util/option_util.h"

using json = nlohmann::json;

std::vector<double> dh_pnl_sims(
    const double S,
    const double K,
    const double r,
    const double T,
    const double sigma,
    const int num_simulations) {
  std::vector<double> pnls;
  double pnl_sum = 0;
  for (int i = 0; i < num_simulations; i++) {
    double nstep = 365.;
    double step = T / nstep;
    double tleft = T;
    double Sstep = S;
    double qty = 0;
    double usd = 0;

    for (int st = 0; st < nstep; ++st) {
      double cd = -call_delta(Sstep, K, r, tleft, sigma);
      double qty_diff = cd - qty;
      double usd_diff = -qty_diff * Sstep;
      qty += qty_diff;
      usd += usd_diff;

      tleft -= step;
      double gaussian = normal_distribution_random();
      Sstep *= std::exp((r - 0.5 * sigma * sigma) * step + sigma * std::sqrt(step) * gaussian);
      // LOG(INFO) << st << " " << Sstep;
    }
    double pnl = usd + qty * Sstep;
    if (Sstep > K) pnl += Sstep - K;
    pnls.push_back(pnl);
    pnl_sum += pnl;
    LOG(INFO) << i << " dh_pnl: " << pnl << " dh_pnl_mean: " << pnl_sum / (i + 1) << " S_last: " << Sstep;
  }
  return pnls;
}

int main(int argc, char* argv[]) {
  InitializeTracer();
  cxxopts::Options opt("option_calculator", "options calculator");
  opt.add_options()("config", "", cxxopts::value<std::string>());
  auto res = opt.parse(argc, argv);
  CHECK(res.count("config"));
  std::ifstream file(res["config"].as<std::string>());
  SPDLOG_INFO(res["config"].as<std::string>());
  json config;
  file >> config;

  const auto& pricing_configs = config["pricing_configs"];
  const auto& opt_configs = config["option_configs"];
  const double S = pricing_configs["stock_price"].get<double>();
  const double sigma = pricing_configs["volatility"].get<double>();
  const int64_t& num_simulations = pricing_configs.at("num_simulations");

  for (const auto& r : pricing_configs["risk_free_rate"]) {
    const double rf = r.get<double>();
    for (const auto& opt_config : opt_configs) {
      const std::string& strike_price = opt_config.at("strike_price");
      const double K = std::stod(strike_price);
      const double T = opt_config["time_to_maturity"].get<double>();

      auto v = dh_pnl_sims(S, K, rf, T, sigma, 1000.);

      LOG(INFO) << "sim price: " << option_price_mc(S, K, rf, T, sigma, num_simulations);
      LOG(INFO) << "bs price: " << call_price(S, K, rf, T, sigma);

      double sum = std::accumulate(v.begin(), v.end(), 0.0);
      double mean = sum / v.size();

      double sq_sum = std::inner_product(v.begin(), v.end(), v.begin(), 0.0);
      double stdev = std::sqrt(sq_sum / v.size() - mean * mean);

      LOG(INFO) << "dh pnl mean: " << mean;
      LOG(INFO) << "dh pnl std: " << stdev;
    }
  }
}